package com.its.common.net.client.handler;


import cn.hutool.core.lang.Assert;
import com.its.common.net.client.Client;
import com.its.common.net.client.config.HeartbeatDetectorDefine;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.ReferenceCountUtil;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.TimeUnit;

/**
 * @Author xiaxp
 * @Date 9/13/22 15:31
 * @Description
 */
@Slf4j
@ChannelHandler.Sharable
public class GenericHeartbeatDetector extends ChannelInboundHandlerAdapter{
    private static final int DEFAULT_PING_INTERVAL = 0;

    private Client client;

    private int heartbeatInterval;

    private HeartbeatDetectorDefine heartbeatDetectorDefine;

    private boolean printSendLog;

    /** 构造方法
     * @param client 客户端
     * @param heartbeatDetectorDefine 心跳间隔，单位秒
     */
    public GenericHeartbeatDetector(Client client, HeartbeatDetectorDefine heartbeatDetectorDefine) {
        Assert.notNull(client, "客户端不能为空");
        this.client = client;
        this.heartbeatDetectorDefine = heartbeatDetectorDefine;
        this.heartbeatInterval = heartbeatDetectorDefine.heartbeatInterval() < 0 ? DEFAULT_PING_INTERVAL : heartbeatDetectorDefine.heartbeatInterval();
        this.printSendLog = heartbeatDetectorDefine.printHeartbeatSendLog();
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        scheduleHeartbeat(ctx);
        ctx.fireChannelActive();
    }

    private void scheduleHeartbeat(ChannelHandlerContext ctx) {
        if(heartbeatInterval > 0) {
            ctx.executor().schedule(() -> {
                Channel channel = ctx.channel();
                if (channel.isActive()) {
                    ByteBuf heartbeatPacket = heartbeatDetectorDefine.generateHeartbeatPacket();
                    channel.writeAndFlush(heartbeatPacket);
                    if (printSendLog) {
                        log.info("[{}] 发送心跳包 = {}", this.getClass().getSimpleName(), heartbeatPacket);
                    }
                    scheduleHeartbeat(ctx);
                }
            }, heartbeatInterval, TimeUnit.SECONDS);
        }
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        if(heartbeatInterval > 0) {
            ctx.executor().schedule(() -> {
                log.warn("[{}] 正在尝试重新连接...", this.getClass().getSimpleName());
                client.connect();
            }, heartbeatInterval, TimeUnit.SECONDS);
        }
        ctx.fireChannelInactive();
    }
}
